package org.ghost.springboot.demo.util;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Time;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

final public class DateUtil {
    /**
     * yyyy/M/d
     */
    public static final String DATE_FORMAT1 = "yyyy/M/d";
    /**
     * yyyy-M-d
     */
    public static final String DATE_FORMAT2 = "yyyy-M-d";
    /**
     *
     G 年代标志符
     y 年
     M 月
     d 日
     h 时 在上午或下午 (1~12)
     H 时 在一天中 (0~23)
     m 分
     s 秒
     S 毫秒
     E 星期
     D 一年中的第几天
     F 一月中第几个星期几
     w 一年中第几个星期
     W 一月中第几个星期
     a 上午 / 下午 标记符
     k 时 在一天中 (1~24)
     K 时 在上午或下午 (0~11)
     z 时区
     */
    /**
     * yyyy.M.d
     */
    public static final String DATE_FORMAT3 = "yyyy.M.d";
    /**
     * yyyy-MM-dd
     */
    public static final String DATE_FORMAT4 = "yyyy-MM-dd";
    /**
     * yyyy/MM/dd
     */
    public static final String DATE_FORMAT5 = "yyyy/MM/dd";
    /**
     * yyyy.MM.dd
     */
    public static final String DATE_FORMAT6 = "yyyy.MM.dd";
    /**
     * yy-M-d
     */
    public static final String DATE_FORMAT7 = "yy-M-d";
    /**
     * yy/M/d
     */
    public static final String DATE_FORMAT8 = "yy/M/d";
    /**
     * yy.M.d
     */
    public static final String DATE_FORMAT9 = "yy.M.d";
    /**
     * yy/MM/dd
     */
    public static final String DATE_FORMA10 = "yy/MM/dd";
    /**
     * MM/dd
     */
    public static final String DATE_FORMA11 = "MM/dd";
    /**
     * HH:mm:ss
     */
    public static final String TIME_FORMAT1 = "HH:mm:ss";
    /**
     * HH:mm
     */
    public static final String TIME_FORMAT2 = "HH:mm";
    /**
     * HH:mm:ss tt
     */
    public static final String TIME_FORMAT3 = "HH:mm:ss a";
    /**
     * HH:mm tt
     */
    public static final String TIME_FORMAT4 = "HH:mm a";
    /**
     * HH:mm:ss
     */
    public static final String TIME_FORMAT5 = "H:mm:ss";
    /**
     * HH:mm
     */
    public static final String TIME_FORMAT6 = "H:mm";
    /**
     * HH:mm:ss SSS
     */
    public static final String TIME_FORMAT7 = "HH:mm:ss SSS";

    private static final Logger logger = LoggerFactory.getLogger(DateUtil.class);
    private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    private static final long EMPTY_LONG = 0L;
    private static final String TIME_SPLIT_CHAR = ":";

    private static final Map<Integer, Integer> DATE_CYCLE_INDEX_MAP = new HashMap<Integer, Integer>() {{
        put(5, 0);
        put(6, 1);
        put(7, 2);
        put(1, 3);
        put(2, 4);
        put(3, 5);
        put(4, 6);
    }};

    public static int getDateCycleIndex(LocalDate localDate) {
        DayOfWeek dayOfWeek = localDate.getDayOfWeek();
        return DATE_CYCLE_INDEX_MAP.get(dayOfWeek.getValue());
    }

    /**
     * 判断新一对三 课程开始时间 是否在当前时间所在周期内 周五到下周四
     *
     * @param now              当前时间 一般为 LocalDate.now
     * @param sessionStartDate 课程开始时间
     * @return
     */
    public static boolean inCurrentCycleWeek(LocalDate now, LocalDate sessionStartDate) {
        int dateCycleIndex = getDateCycleIndex(now);
        LocalDate sDate = now.minusDays(dateCycleIndex);
        LocalDate eDate = now.plusDays(7 - dateCycleIndex);
        if (sessionStartDate.isBefore(eDate) && sessionStartDate.isAfter(sDate)) {
            return true;
        }
        return false;
    }

    public static List<LocalDateTime> getNextWeekList(LocalDateTime baseDateTime, Integer count) {
        List<LocalDateTime> ret = new LinkedList<>();
        for (int i = 0; i < count; i++) {
            LocalDateTime localDateTime = baseDateTime.plusDays(i * 7);
            ret.add(localDateTime);
        }
        return ret;
    }

    public static List<LocalDateTime> getWeekList(LocalDate beginDate, List<LocalTime> timeList, Integer week, Integer count) {
        List<LocalDateTime> ret = new LinkedList<>();
        LocalDate now = beginDate;
        if (week == 0) {
            week = 7;
        }
        while (now.getDayOfWeek().getValue() != week) {
            now = now.plusDays(1);
        }
        for (int i = 0; i < count; i++) {
            LocalDate newDate = now.plusDays(i * 7);
            for (LocalTime localTime : timeList) {
                LocalDateTime localDateTime = newDate.atTime(localTime);
                ret.add(localDateTime);
            }
        }
        return ret;
    }

    public static List<LocalDateTime> getWeekList(LocalDate beginDate, String times, Integer week, Integer count) {
        List<LocalTime> timeList = Arrays.stream(times.split(",")).map(time -> LocalTime.parse(time)).collect(Collectors.toList());
        return getWeekList(beginDate, timeList, week, count);
    }

    /**
     * 是否为空
     *
     * @param date 日期
     * @return 是否为空
     */
    public static Boolean isEmpty(final Date date) {
        return date == null || EMPTY_LONG == date.getTime();
    }

    /**
     * 如果值为null，获取为当前时间的默认值
     *
     * @param date 值
     * @return Date
     */
    public static Date getDefaultValue(final Date date) {
        return getDefaultValue(date, new Date());
    }

    /**
     * 如果值为null，获取默认值
     *
     * @param date         值
     * @param defaultValue 默认值
     * @return Date
     */
    public static Date getDefaultValue(final Date date, Date defaultValue) {
        return date != null ? date : defaultValue;
    }

    /**
     * 获取当前时间
     *
     * @return 当前时间
     */
    public static Date now() {
        return new Date();
    }

    /**
     * 获取明天
     *
     * @return 明天
     */
    public static Date tomorrow() {
        Date now = new Date();
        Calendar cal = Calendar.getInstance();
        cal.setTime(now);
        cal.add(Calendar.DAY_OF_YEAR, 1);
        return cal.getTime();
    }

    /**
     * 获取之前的几天
     *
     * @return 明天
     */
    public static Date beforeDay(int beforeDayCount) {
        Date now = new Date();
        Calendar cal = Calendar.getInstance();
        cal.setTime(now);
        cal.add(Calendar.DAY_OF_YEAR, -beforeDayCount);
        return cal.getTime();
    }

    /**
     * 获取未来的几天
     *
     * @return 明天
     */
    public static Date Day(int nextDayCount) {
        Date now = new Date();
        Calendar cal = Calendar.getInstance();
        cal.setTime(now);
        cal.add(Calendar.DAY_OF_YEAR, nextDayCount);
        return cal.getTime();
    }

    /**
     * 获取当前时间
     *
     * @return 当前时间
     */
    public static String nowStr() {
        return DateFormatUtils.ISO_DATE_FORMAT
                .format(new Date());
    }

    /**
     * 获取明天
     *
     * @return 明天
     */
    public static String tomorrowStr() {
        Date now = new Date();
        Calendar cal = Calendar.getInstance();
        cal.setTime(now);
        cal.add(Calendar.DAY_OF_YEAR, 1);
        return DateFormatUtils.ISO_DATE_FORMAT.format(cal.getTime());
    }

    /**
     * 日期字符串转换为Date
     *
     * @param dateStr 日期字符串
     * @return Date
     */
    public static Date toDate(String dateStr) {
        if (StringUtils.isNotBlank(dateStr)) {
            if (dateStr.contains(" ")) {
                //包含空格-含时分秒
                if (dateStr.contains("-")) {
                    return toDate(dateStr, DATE_FORMAT4 + " " + TIME_FORMAT1);
                } else if (dateStr.contains("/")) {
                    return toDate(dateStr, DATE_FORMAT5 + " " + TIME_FORMAT1);
                } else if (dateStr.contains(".")) {
                    return toDate(dateStr, DATE_FORMAT6 + " " + TIME_FORMAT1);
                }
            } else {
                if (dateStr.contains("-")) {
                    return toDate(dateStr, DATE_FORMAT4);
                } else if (dateStr.contains("/")) {
                    return toDate(dateStr, DATE_FORMAT5);
                } else if (dateStr.contains(".")) {
                    return toDate(dateStr, DATE_FORMAT6);
                }
            }
        }
        return null;
    }


    public static Date toDate(String dateStr, String format) {
        Date date = null;
        try {
            date = DateUtils.parseDate(dateStr, format);
        } catch (ParseException | IllegalArgumentException e) {
            if (logger.isWarnEnabled()) {
                logger.warn("日期时间解析失败: {}, {}", e.getMessage(), e);
            }
        }

        return date;
    }

    public static Date getWeekStartTime() {
        Calendar cal = Calendar.getInstance();
        int day_of_week = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (day_of_week == 0) {
            day_of_week = 7;
        }
        cal.add(Calendar.DATE, -day_of_week + 1);
        return cal.getTime();
    }

    public static Date getWeekEndTime() {
        Calendar cal = Calendar.getInstance();
        int day_of_week = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (day_of_week == 0) {
            day_of_week = 7;
        }
        cal.add(Calendar.DATE, -day_of_week + 7);
        return cal.getTime();
    }

    public static String dateToString(Date date, String pattern) {
        if (date != null && StringUtils.isNotBlank(pattern)) {
            SimpleDateFormat format = new SimpleDateFormat(pattern);
            return format.format(date);
        }
        return "";
    }

    public static String dateToString(LocalDateTime date, String pattern) {
        if (date != null) {
            DateTimeFormatter format = DateTimeFormatter.ofPattern(pattern);
            return format.format(date);
        }
        return null;
    }

    public static String dateToYMD(Date date) {
        if (date != null) {
            SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT4);
            return format.format(date);
        }
        return null;
    }

    public static String dateToYMDHMS(Date date) {
        if (date != null) {
            SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT4 + " " + TIME_FORMAT1);
            return format.format(date);
        }
        return "";
    }

    public static String dateToYMDHMSSSS(Date date) {
        if (date != null) {
            SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT4 + " " + TIME_FORMAT7);
            return format.format(date);
        }
        return "";
    }

    public static String dateToYMD(LocalDateTime dateTime) {
        if (dateTime != null) {
            DateTimeFormatter df = DateTimeFormatter.ofPattern(DATE_FORMAT4);
            return df.format(dateTime);
        }
        return "";
    }

    public static String dateToYMD(LocalDate date) {
        if (date != null) {
            DateTimeFormatter df = DateTimeFormatter.ofPattern(DATE_FORMAT4);
            return df.format(date);
        }
        return "";
    }

    public static String dateToYMDHMS(LocalDateTime dateTime) {
        if (dateTime != null) {
            DateTimeFormatter df = DateTimeFormatter.ofPattern(DATE_FORMAT4 + " " + TIME_FORMAT1);
            return df.format(dateTime);
        }
        return null;
    }

    public static DayOfWeek dateToWeekDay(Date date) {
        LocalDateTime dateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
        return dateTime.getDayOfWeek();
    }

    public static String timeToHHMMSS(LocalTime localTime) {
        if (localTime != null) {
            DateTimeFormatter df = DateTimeFormatter.ofPattern(TIME_FORMAT1);
            return df.format(localTime);
        }

        return null;
    }

    public static String timeToHHMMSS(Time time) {
        if (time != null) {
            SimpleDateFormat df = new SimpleDateFormat(TIME_FORMAT1);
            return df.format(time);
        }

        return null;
    }

    public static String timeToHHMM(LocalTime localTime) {
        if (localTime != null) {
            DateTimeFormatter df = DateTimeFormatter.ofPattern(TIME_FORMAT2);
            return df.format(localTime);
        }
        return null;
    }

    public static String timeToStr(LocalTime localTime, String pattern) {
        if (localTime != null) {
            DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern);
            return df.format(localTime);
        }
        return null;
    }

    public static Date localDateTimeToDate(LocalDateTime localDateTime) {
        ZoneId zoneId = ZoneId.systemDefault();
        ZonedDateTime zdt = localDateTime.atZone(zoneId);
        return Date.from(zdt.toInstant());
    }

    public static LocalDateTime dateToLocalDateTime(Date date) {
        if (date != null) {
            return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        }
        return null;
    }

    /**
     * 解析9:30:00格式字符串
     *
     * @param input
     * @return
     */
    public static LocalTime strToLocalTime(String input) {
        if (StringUtils.isNotBlank(input)) {
            String[] array = input.split(TIME_SPLIT_CHAR);
            if (array.length >= 2) {
                int hour = NumberUtil.toInt(array[0], 0);
                int minute = NumberUtil.toInt(array[1], 0);
                int second = 0;
                if (array.length >= 3) {
                    second = NumberUtil.toInt(array[2], 0);
                }

                return LocalTime.of(hour, minute, second);
            }
        }

        return null;
    }

    public static String dateToYMDHMWeek(Date date) {
        if (date != null) {
            SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT4 + " " + TIME_FORMAT2);
            return format.format(date) + " " + dateToWeekDayName(date);
        }

        return "";
    }

    public static String dateToWeekDayName(Date date) {
        DayOfWeek dayOfWeek = dateToWeekDay(date);
        String result = "";
        switch (dayOfWeek) {
            case MONDAY:
                result = "周一";
                break;
            case TUESDAY:
                result = "周二";
                break;
            case WEDNESDAY:
                result = "周三";
                break;
            case THURSDAY:
                result = "周四";
                break;
            case FRIDAY:
                result = "周五";
                break;
            case SATURDAY:
                result = "周六";
                break;
            case SUNDAY:
                result = "周日";
                break;
            default:
                result = "未知";
                break;
        }

        return result;
    }

}

